home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 July: Mac OS SDK / Dev.CD Jul 96 SDK / Dev.CD Jul 96 SDK1.toast / Development Kits (Disc 1) / OpenDoc / OpenDoc Development / Debugging Support / OpenDoc Source Code / Storage / Bento / TargtHdr.c < prev    next >
Encoding:
Text File  |  1996-04-22  |  33.2 KB  |  687 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        TargtHdr.c
  3.  
  4.     Contains:    Target Handler for Bento Container Suite
  5.  
  6.     Owned by:    Vincent Lo, Ed Lai
  7.  
  8.     Copyright:    © 1993 - 1996 by Apple Computer, Inc., all rights reserved.
  9.  
  10.     Change History (most recent first):
  11.  
  12.          <2>     1/15/96    TJ        Cleaned Up
  13.          <5>      4/7/95    EL        Check the label rather than the passed in
  14.                                     flag for endian-ness.
  15.          <4>     3/10/95    EL        1226127: move scanning backwards for label
  16.                                                     code to FSHdr.cpp.
  17.          <3>     9/29/94    RA        1189812: Mods for 68K build.
  18.          <2>     8/26/94    EL        #1182308 Allows non-byte swapping
  19.                                                     format/extract.
  20.          <1>     5/27/94    VL        first checked in
  21.  
  22.     To Do:
  23. */
  24.  
  25. /* (use tabs = 2 to view this file correctly) */
  26. /*---------------------------------------------------------------------------*
  27.  |                                                                           |
  28.  |                     <<< TargetContainerHandlers.c >>>                     |
  29.  |                                                                           |
  30.  |            Container Manager Basic Updating Container Handlers            |
  31.  |                                                                           |
  32.  |                               Ira L. Ruben                                |
  33.  |                                  5/28/92                                  |
  34.  |                                                                           |
  35.  |                     Copyright Apple Computer, Inc. 1992                   |
  36.  |                           All rights reserved.                            |
  37.  |                                                                           |
  38.  *---------------------------------------------------------------------------*
  39.  
  40.  This file contains an the set of basic container handlers and its metahandler used by the
  41.  Container Manager when doing updating operations on a target container.  The "I/O" done
  42.  here is in terms of Container Manager API calls on a dynamic OR real value (more about
  43.  this later).
  44.  
  45.  In order to understand how the handlers in this file are used it is necessary to 
  46.  understand their context.  Remember, when CMOpen[New]Container() is called, a type name
  47.  is always passed so that the Container Manager can find the metahandler/type association
  48.  which will yield the I/O handlers that operate upon the container to be opened.  That is
  49.  always true; there are a set of container I/O handlers associated with every open
  50.  container.  ExampleHandlers.c is such a set.
  51.  
  52.  When the parameters to CMOpenNewContainer() are such as to indicate that ANOTHER (called
  53.  the "target") container is to be updated (kCMUpdateTarget useFlags), then the target is
  54.  opened by the Container Manager, NOT explicitly by the API user.  Note, this is only the
  55.  case for CMOpenNewContainer() and not CMOpenContainer(), because the intent is to record
  56.  the updating operations in a separate container without actually modifying the target. 
  57.  Thus the updating operations are recorded, obviously in a container opened for writing
  58.  (hence CMOpenNewContainer()), while the target is always opened for reading 
  59.  (CMOpenContainer()).
  60.  
  61.  There is also a case where you can append the updates to the SAME container.  This is 
  62.  referred to as update-by-append (CMOpenNewContainer() with kCMUpdateByAppend useFlags).
  63.  In this case, all updates are appended to an existing container, and an additional TOC
  64.  is layered on to the end of the container when closed (something like opening a data file
  65.  to be made into a continer by opening it for converting -- kCMConverting useFlags).  Each
  66.  time the container is opened and then closed for update-by-append, the new updates and a
  67.  new TOC are appended.  Whenever such a container is opened (by any mode), all the updates
  68.  are applied appropriately to the original container.  Conceptually, you can always look
  69.  at it as an updating container opened for writing and the target container opened for
  70.  reading.
  71.  
  72.  The target container is opened by the Container Manager using CMOpenContainer() just as
  73.  the API user would do it.  It's done internally, so there are other "gyrations" going on.
  74.  But the bottom line is that there still must be a metahandler/type association as usual.
  75.  The type for the targets is always CMTargetHandlersTypeName (a macro).  The methandler
  76.  associated with this type is the one defined in this file to yield the handlers to be
  77.  used to read the target container.  The metahandler/type association still must be done
  78.  as usual by the API user via CMSetMetaHandler().
  79.  
  80.  In summary, there are a set of standard handlers for the updating container which are
  81.  whatever they are.  Then there is the set of handlers defined in this file which are
  82.  used to read a target container.  The context for this file should now be clear!
  83.  
  84.  Now, some more details about the updating and how these handlers are used.
  85.  
  86.  Containers are updated by a series of instructions contained in a TOC.  The TOCs may be
  87.  part of other containers or separate (layered) TOCs in the same container that is to be
  88.  updated.  In either case, the Container Manager "gets at" what is to be updated (the
  89.  target container) through a special value contained in the updating TOC.  This value
  90.  belongs to a unique property of TOC ID 1 and is referred to here as the "pointing value"
  91.  (because it "points" in some fashion to the target container to be updated).
  92.  
  93.  Because there are two cases (i.e., container to be updated is the same or a different
  94.  container), the pointing value takes two forms:
  95.  
  96.  (1) For updating the SAME container - the value is a "real" value whose offset and length
  97.           info (i.e., its value data) is the entire container to be updated (including its
  98.          label).  The value belongs to the updating container and hence a distinct laryered
  99.          TOC.  It is used by the target by the handlers in this file.
  100.          
  101.  (2) For updating a SEPARATE container - the value is a real base value of some user
  102.           defined  type that will spawn a dynamic value if a CMUseValue() or CMNewValue() is
  103.          done on it.  Again, the value belongs to the updating container and is used by the
  104.          target by the handlers in this file.
  105.  
  106.  As discussed above, the context in which the handlers in this file are used are for use
  107.  by the target container (the one opened implicitly as a result of opening the updating
  108.  container).  They do this by mapping the container operations on to standard value API
  109.  calls using the pointing value (e.g., CMReadValueData()).  Thus for case (1), a real value
  110.  for layered TOC of the same container, calls back to the API would end up calling the
  111.  handlers for the updating container.  For case (2), a  dynamic value, the dynamic value's
  112.  handler package would be called to do whatever needs to be done to "get at" the target
  113.  container.
  114.  
  115.  In other words, the handlers for a target container, always use the value for one layer
  116.  "up", its updater.  For multiple updaters reading from a target will work its way back
  117.  up the chain of values doing their operatations in terms of the next value up.  This will
  118.  continue until a top-most value is reached.  That top-most value will either be one for
  119.  the top-most updating container or a dynamic value.  If not dynamic, the top-most value
  120.  will use the handlers for that container (i.e., "real" handlers like those of 
  121.  ExampleHandlers.c).  For dynamic values, it would be the dynamic value-s handler package.
  122.   
  123.  Unlike the other example handler packages, the ones defined here MUST be done in terms of
  124.  an interface conventions known to the Container Manager.  Thus, this handler package is
  125.  NOT (repeat NOT) in the "example" category as all the other handler packages are!  Its
  126.  interface conventions are not subject to change.  However, just like the other example
  127.  handlers, the code must be published, and for the same reasons.  Namely, different
  128.  hardware may required modifications to the code (mainly in the TOC and label I/O
  129.  handlers).
  130.  
  131.  In order to accomplish these goals, the metahandler defined in this file must be
  132.  associated with the PREDEFINED type CMTargetHandlersTypeName (a macro in the header for
  133.  this file).  This is done by the API user in the standard way using CMSetMethandler().
  134.  
  135.  There is no explicit routine to set up the refCon.  Here we take advantage of the
  136.  "attributes" mechanism passed to CMOpen[New]Container().  This is where the "attributes"
  137.  (assumed a form of refCon) is passed as-is to the open handler.  The open handler, in
  138.  turn, returns the refCon to be passed among all the other handlers.  So all that the
  139.  Container Manager needs to do is pass the pointing value in the "attributes".  From that
  140.  the open handler can build the refCon.  This is one of the interface conventions assumed
  141.  by the Container Manager for the CMTargetHandlersTypeName.
  142.    
  143.  Since this file is not an example, the commenting is not done in the narative form used
  144.  in the examples and handlers not needed are not included.  Indeed, targets are only
  145.  opened for reading, and thus no writing handlers are needed.  You should be looking at
  146.  ExampleHandlers.c, not this file, for the documentation on how to write container
  147.  handlers.
  148.  
  149.  There is one final thing to point out here.  If you compare the code in this file with
  150.  the code for embedded handlers, ExampleEmbeddedHandlers.c, you will see they are very
  151.  similar.  Indeed, ExampleEmbeddedHandlers.c was used as the basis for this file!  Here,
  152.  and there, all operations are done in terms of value operations on a base value.  For
  153.  embedded containers the value is one already in the container.  Here, for case (1) at
  154.  least, the value is also in the same container, although in a different TOC.  Case (2)
  155.  has no counterpart with embeddeds.  But if you associate a dynamic value with an
  156.  embedded container the effect is the same.
  157.  
  158.  When you stop to think of it, for case (1) at least, it makes sense that we would be
  159.  similar to embedded containers.  The reason we don't use that code is because of the
  160.  reasons we mentioned above, i.e., the interface must be known by the Container Manager.
  161.  Also we wanted to fully document an additional example of container handlers.
  162.  
  163.  There are some other subtle changes from ExampleEmbeddedHandlers.c.  First, we let the
  164.  error reporter return instead of aborting execution.  The caller's return something
  165.  "reasonable" to the Container Manager to take appropriate action.  Second, there is
  166.  no returnParentValue_Handler() since this is not a true embedded container case. 
  167.  Basically all that means is that the close of updating containers is handled "normally".
  168.  Closes of embedded containers involve possible closing of all descendents in a tree of
  169.  nested embedded containers. Finally, third, and not so subtle, as mentioned above, there
  170.  are no handlers that do writing, since targets are always opened for reading.
  171. */
  172.  
  173.  
  174. #include <stddef.h>
  175. #include <stdlib.h>
  176. #include <stdio.h>
  177. #include <string.h>
  178. #include <stdarg.h>
  179.  
  180. #include "CMAPI.h"
  181. #include "TargtHdr.h"
  182.  
  183.  
  184. /*--------------------------------------------------------------------------*
  185.  | targetContainerMetahandler - metahandler to return the handler addresses |
  186.  *--------------------------------------------------------------------------*
  187.  
  188.  This is the metahandler that CMOpen[New]Container() associates with a type passed to the
  189.  target CMOpenContainer().  Here are the requisite forward declarations to those handlers
  190.  for the proc pointer table defined in the code below.                                                                    */
  191.  
  192.                                                                  CM_CFUNCTIONS
  193.                                                                 
  194. CMHandlerAddr CM_FIXEDARGS targetContainerMetahandler(CMType targetType, CMconst_CMGlobalName operationType);
  195.  
  196. static CMRefCon open_Handler(CMRefCon attributes, CMOpenMode mode);
  197. static void close_Handler(CMRefCon refCon);
  198. static CMSize seek_Handler(CMRefCon refCon, CM_LONG posOff, CMSeekMode mode);
  199. static CMSize tell_Handler(CMRefCon refCon);
  200. static CMSize read_Handler(CMRefCon refCon, CMPtr buffer, CMSize elementSize, CMCount theCount);
  201. static CMSize containerSize_Handler(CMRefCon refCon);
  202. static void readLabel_Handler(CMRefCon refCon, CMMagicBytes magicByteSequence,
  203.                                                              CMContainerFlags *flags, CM_USHORT *bufSize,
  204.                                                              CM_USHORT *majorVersion, CM_USHORT *minorVersion,
  205.                                                              CMSize *tocOffset, CMSize *tocSize);
  206. static CM_UCHAR *returnContainerName_Handler(CMRefCon refCon);
  207. static void extractData_Handler(CMRefCon refCon, CMDataBuffer buffer,
  208.                                                                   CMSize size, CMPrivateData data);
  209. static void formatData_Handler(CMRefCon refCon, CMDataBuffer buffer, CMSize size, CMPrivateData data);
  210.  
  211.                                                             CM_END_CFUNCTIONS
  212.                                                                                                                                                                                 /*
  213.  See comments in CM_API_Environment.h about CM_CFUNCTIONS/CM_END_CFUNCTIONS (used only
  214.  when compiling under C++).
  215.  
  216.  Note, the only action handlers we need are for input since these handlers are only used
  217.  for reading the target container.  formatData_Handler() is generally considered as an
  218.  output handler, but is used by the Container Manager for converting CMReference keys to
  219.  internal hardware representation.
  220. */
  221.  
  222. CMHandlerAddr CM_FIXEDARGS targetContainerMetahandler(CMType targetType, CMconst_CMGlobalName operationType)
  223. {
  224.     static char *operationTypes[] = {CMOpenOpType,                    /*  0 */ /*  Operation Types    */
  225.                                                                      CMCloseOpType,                    /*  1 */
  226.                                                                      CMFlushOpType,                    /*  2 */
  227.                                                                      CMSeekOpType,                    /*  3 */
  228.                                                                      CMTellOpType,                    /*  4 */
  229.                                                                      CMReadOpType,                    /*  5 */
  230.                                                                      CMWriteOpType,                    /*  6 */
  231.                                                                      CMEofOpType,                        /*  7 */
  232.                                                                      CMTruncOpType,                    /*  8 */
  233.                                                                      CMSizeOpType,                    /*  9 */
  234.                                                                      CMReadLblOpType,                /* 10 */
  235.                                                                      CMWriteLblOpType,            /* 11 */
  236.                                                                      CMParentOpType,                /* 12 */
  237.                                                                      CMContainerOpName,            /* 13 */
  238.                                                                      CMTargetTypeOpType,        /* 14 */
  239.                                                                      CMExtractDataOpType,        /* 15 */
  240.                                                                      CMFormatDataOpType,        /* 16 */
  241.                                                                      NULL};
  242.     char      **t;
  243.     CMType ignored = targetType;
  244.     
  245.     /* Look up the operation type in the operationTypes table above...                                        */
  246.     
  247.     t = operationTypes - 1;
  248.     while (*++t) if (strcmp((char *)operationType, *t) == 0) break;
  249.  
  250.     /* Now that we got it (hopefully), return the appropriate routine address...                    */
  251.     
  252.     switch (t - operationTypes) {
  253.         case  0:    return ((CMHandlerAddr)open_Handler);                                /*CMOpenOpType                 */
  254.         case  1:    return ((CMHandlerAddr)close_Handler);                            /*CMCloseOpType             */
  255.         case  3:    return ((CMHandlerAddr)seek_Handler);                                /*CMSeekOpType                 */
  256.         case  4:    return ((CMHandlerAddr)tell_Handler);                                /*CMTellOpType                 */
  257.         case  5:    return ((CMHandlerAddr)read_Handler);                                /*CMReadOpType                 */
  258.         case  9:    return ((CMHandlerAddr)containerSize_Handler);            /*CMSizeOpType                 */
  259.         case 10:    return ((CMHandlerAddr)readLabel_Handler);                    /*CMReadLblOpType         */
  260.         case 13:    return ((CMHandlerAddr)returnContainerName_Handler);/*CMContainerOpName        */
  261.         case 15:    return ((CMHandlerAddr)extractData_Handler);                /*CMExtractDataOpType    */
  262.         case 16:    return ((CMHandlerAddr)formatData_Handler);                    /*CMFormatDataOpType    */
  263.         
  264.         default:    return (NULL);
  265.     }
  266. }
  267.  
  268. static void ODFlipMove(CMPtr from, CMPtr to, CM_LONG size)
  269. {
  270.     char    *dest = (char *)to + size - 1;
  271.     char    *src = (char *)from;
  272.     
  273.     while (size--)
  274.         *dest-- = *src++;            
  275. }
  276.  
  277. /*----------------------------------------------------------------*
  278.  | open_Handler - open a container for input, output, or updating |
  279.  *----------------------------------------------------------------*
  280.  
  281.  Called to open the container associated with the specified refCon.  The open modes passed
  282.  are "wb+" (truncate and updating) for creating a new container, "rb" for reading an
  283.  existing container, and "rb+" (updating) for converting a "bunch" of data in a file to
  284.  container format.
  285.  
  286.  The open routine is unique among the handlers in that the potential interpretion of the
  287.  first argument is different.  The attributes are passed to CMOpen[New]Container() and
  288.  then passed to here unaltered and not looked at by the API.  It is intended to tie the
  289.  open handler to a specific container.  Thus the attributes serves as a communication
  290.  channel strictly to the open.  In the case of updating containers, the attributes is the
  291.  pointing value (i.e., a CMValue).  As discussed at the start of this file it will be 
  292.  either a real or a dynamic value. 
  293.  
  294.  What the API expects as a return value is a "refCon" to be passed unaltered to all of the
  295.  other handler routines.  This too is a communication channel that ties the handlers to
  296.  their open.  Using the passed pointing value, we return a refCon that has the following
  297.  layout:                                                                                                                                                                */
  298.  
  299.  struct RefCon {                                                                    /* The refCon layout:                                    */
  300.      CMSession         sessionData;                                            /*        ptr to current container session*/
  301.      CMValue           value;                                                        /*         pointing value to operate upon    */
  302.      CMBoolean         isReverseEndian;                                    /*    whether to use reverse endian        */
  303.      unsigned long position;                                                /*         current position within value        */
  304.      unsigned long size;                                                        /*        value size                                            */
  305.      char                  typeName[1];                                            /*        start of value's type name            */
  306.  };
  307.  typedef struct RefCon RefCon, *RefConPtr;                                                                                            /*
  308.  
  309.  The current session data pointer, which we need to allocate the refCon is kept just as    a
  310.  convenience in case we need it again.  At the very least it will be needed to free the
  311.  refCon.
  312.  
  313.  Since we are simulating "I/O" with these handlers, we need to keep track of the a current
  314.  "stream" position within the value. The position and size fields in the struct serves this
  315.  purpose.  The typeName of the value is used as an error insert if we report an error.
  316. */
  317.  
  318. static CMRefCon open_Handler(CMRefCon attributes, CMOpenMode mode)
  319. {
  320.     CMValue            value = (CMValue)attributes;
  321.     CMContainer    container;
  322.     CMSession        sessionData;
  323.     CMType            type;
  324.     RefConPtr     p;
  325.     char                *typeName;
  326.     
  327.     /* Get the type name for the value and get current session pointer...                                    */
  328.     
  329.     CMGetValueInfo(value, &container, NULL, NULL, &type, NULL);
  330.     sessionData = CMGetSession(container);
  331.     
  332.     typeName = (char *)CMGetGlobalName(type);            /* get type name for errors                         */
  333.     if (typeName == NULL) {                                                /* no global name?                                            */
  334.         CMError(sessionData, "Unable to get type name for pointing value in container \"^0\"!", CMReturnContainerName(container));
  335.         return (NULL);            
  336.     }
  337.  
  338.     /* Allocate and fill in the refCon we will return to the Container Manager...                    */
  339.     
  340.     p = (RefConPtr)CMMalloc(sizeof(RefCon) + strlen(typeName), sessionData);
  341.     if (p == NULL) {                                                            /* allocation failed!                                        */
  342.         CMError(sessionData, "Allocation of \"refCon\" failed for target (type \"^0\", container \"^1\")!", typeName, CMReturnContainerName(container));
  343.         return (NULL);
  344.     }
  345.     
  346.     p->sessionData = sessionData;                                        /* save the current session ptr                */
  347.     p->value = value;                                                                /* value we will do operations on            */
  348.     p->isReverseEndian = 0;                                                    /* assume same endian-ness                        */
  349.     p->size     = (unsigned long)CMGetValueSize(value);/* value's size                                                */
  350.     strcpy(p->typeName, typeName);                                    /* copy in the global type name                */
  351.  
  352.     /* Check the open mode and do appropriate checks on size. Set appropriate position...    */
  353.  
  354.     if (strcmp((char *)mode, "wb+") == 0) {                /* writing...                                                         */
  355.         if (p->size != 0) {                                                    /* size must be zero                                        */
  356.             CMError(sessionData, "Cannot create target container for a pointing value (type \"^0\") that already has data!", p->typeName);
  357.             CMFree(p, sessionData);
  358.             return (NULL);
  359.         }
  360.         p->position = 0;                                                        /* position to 1st free byte                        */
  361.     } else if (strcmp((char *)mode, "rb") == 0) { /* reading...                                                         */
  362.         if (p->size == 0) {                                                    /* size must be non-zero                                */
  363.             CMError(sessionData, "Cannot read target container for a pointing value (type \"^0\") that doesn't have data!", p->typeName);
  364.             CMFree(p, sessionData);
  365.             return (NULL);
  366.         }
  367.         p->position = 0;                                                        /* position to 1st byte to read                    */
  368.     } else if (strcmp((char *)mode, "rb+") == 0)  /* converting or updating...                        */
  369.         p->position = p->size;                                            /* size can be anything(position at end)*/
  370.     else {                                                                                /* bad mode...                                                    */
  371.         CMError(sessionData, "Invalid target container open mode (\"^0\", pointing value type \"^1\")!", (char *)mode, p->typeName);
  372.         CMFree(p, sessionData);
  373.         return (NULL);
  374.     }
  375.     
  376.     return ((CMRefCon)p);
  377. }
  378.  
  379.  
  380. /*------------------------------------------*
  381.  | close_Handler - close the container file |
  382.  *------------------------------------------*
  383.  
  384.  Called when CMCloseContainer() is called.  The space allocated by the refCon is freed.
  385. */
  386.  
  387. static void close_Handler(CMRefCon refCon)
  388. {
  389.     RefConPtr p = (RefConPtr)refCon;
  390.     CMSession sessionData = p->sessionData;
  391.     
  392.     CMFree(p, sessionData);                                                                /* bye, bye, refcon...                    */
  393. }
  394.  
  395.  
  396. /*-------------------------------------------------------------------*
  397.  | seek_Handler - position the I/O to the specified container offset |
  398.  *-------------------------------------------------------------------*
  399.  
  400.  This allows the API random access to the target container. The current position value is
  401.  set according to the mode and posOff.  0 is returned for success and non-zero for failure.
  402.  
  403.  The mode determines how to use posOff to set the position. It is defined as one of the
  404.  following seek modes (defined in CM_API_Types.h which is included by CM_API.h):
  405.  
  406.     kCMSeekSet                    Set the position to the value offset specified by posOff.  The posOff
  407.                                             is the character offset from the beginning of the value data.
  408.     
  409.     kCMSeekCurrent            Set the stream posOff characters from the current position.  posOff
  410.                                             can be postive or negative here.
  411.                                             
  412.     kCMSeekEnd                    Set the position posOff characters from the current end of the value.
  413.                                             posOff here is only zero or negative.  We never assume or allow
  414.                                             undefined contents to be generated in a container. This makes life
  415.                                             easier here since we don't have to check for this.
  416.  
  417.  Note, here all we do is set our position saved in the refCon.  There is nothing else to
  418.  do.  The position is used by other handlers such as the reads and writes.
  419. */
  420.  
  421. static CMSize seek_Handler(CMRefCon refCon, CM_LONG posOff, CMSeekMode mode)
  422. {
  423.     RefConPtr p = (RefConPtr)refCon;
  424.     
  425.     if (mode == kCMSeekSet)                                                    /* kCMSeekSet...                                            */
  426.         p->position = (unsigned long)posOff;                    /* ...just use posOff as is                        */
  427.     else if (mode == kCMSeekEnd)                                        /* kCMSeekEnd...                                            */
  428.         p->position = (long)p->size + posOff;                    /* ...posOff is 0 or negative here        */
  429.     else                                                                                         /* kCMSeekCurrent...                                     */
  430.         p->position = (long)p->position + posOff;            /* ...relative positioning                        */
  431.         
  432.     return (0);
  433. }
  434.  
  435.  
  436. /*-----------------------------------------------*
  437.  | tell_Handler - return current stream position |
  438.  *-----------------------------------------------*
  439.  
  440.  Returns the current position as characters from the start of the target container.
  441.  The position is set by other handlers like the seek (above) and the reads and writes.
  442. */
  443.  
  444. static CMSize tell_Handler(CMRefCon refCon)
  445. {
  446.     return ((CMSize)((RefConPtr)refCon)->position);
  447. }
  448.  
  449.  
  450. /*------------------------------------------------------------------*
  451.  | read_Handler - read information from the container into a buffer |
  452.  *------------------------------------------------------------------*
  453.  
  454.  Reads bytes from the current position with the target container value into the specified
  455.  buffer.  Up to theCount elements of the specified size are read.  The actual number of
  456.  items read, which may be less than theCount (e.g., if the end-of-container is read) or
  457.  even 0 is returned as the function result.
  458. */
  459.  
  460. static CMSize read_Handler(CMRefCon refCon, CMPtr buffer, CMSize elementSize, CMCount theCount)
  461. {
  462.     RefConPtr            p = (RefConPtr)refCon;
  463.     unsigned long amountRead;
  464.     
  465.     amountRead = (unsigned long)CMReadValueData(p->value,                 /* read value...                */
  466.                                                                                              buffer,
  467.                                                                                              p->position, 
  468.                                                                                              (CMSize)((unsigned long)elementSize * (unsigned long)theCount));
  469.     
  470.     p->position += amountRead;                                        /* update position by amount read                */
  471.     amountRead  /= (unsigned long)elementSize;        /* convert back to elementSize units        */
  472.         
  473.     return ((CMSize)amountRead);        
  474. }
  475.  
  476.  
  477. /*------------------------------------------------------------------*
  478.  | containerSize_Handler - return the current size of the container |
  479.  *------------------------------------------------------------------*
  480.  
  481.  Returns the 1-relative size of the value (i.e., target container).
  482. */
  483.  
  484. static CMSize containerSize_Handler(CMRefCon refCon)
  485. {
  486.     RefConPtr p = (RefConPtr)refCon;
  487.     
  488.     p->position = p->size;                                                                /* moral equivalent of seek/tell*/
  489.     
  490.     return ((CMSize)p->size);
  491. }
  492.  
  493.  
  494. /*---------------------------------------------------------------*
  495.  | readLabel_Handler - read in and extract the container's label |
  496.  *---------------------------------------------------------------*
  497.  
  498.  Finds the label in the target container value and returns all the information it contains.
  499.  In order to make target container values have the same format as the stream containers
  500.  which the ExampleHandlers.c handlers maintain, we will put and look for the label at the
  501.  end of the target container value.
  502.  
  503.  As defined by the API documentation, a container label has the following layout:
  504.  
  505.             +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  506.             |      Magic Bytes      |flags|bufsz|major|minor|TOC offset |  TOC size |
  507.             +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  508.                                     8              2     2     2     2        4           4
  509.  
  510.  For C compilers that generate code for machines where char is 1 byte, short 2, and long
  511.  4, we may use the following struct to define the label format:                                                    */
  512.  
  513.  struct ContainerLabelFmt {                                /* Layout of a container label:                                */
  514.      unsigned char  magicBytes[8];                    /* 8 bytes: the magic byte identifier                    */
  515.      unsigned short flags;                                    /* 2                the label flags                                        */
  516.      unsigned short bufSize;                                /* 2                TOC buffer size / 1024                        */
  517.      unsigned short majorVersion;                        /* 2                major format version number                */
  518.      unsigned short minorVersion;                        /* 2                minor format version number                */
  519.      unsigned long    tocOffset;                            /* 4                offset to start of TOC                        */
  520.      unsigned long    tocSize;                                /* 4                total byte size of the TOC                */
  521.  };
  522.  typedef struct ContainerLabelFmt ContainerLabelFmt, *ContainerLabelFmtPtr;                            /*
  523.  
  524.                                     W A R N I N G !!
  525.  
  526.  If your C compiler and/or hardware cannot use the above struct, then this handler will
  527.  have to be recoded accordingly.  Indeed, it is for this very reason this IS a handler. It
  528.  shifts the portability problem outside the API.
  529.  
  530.  This handler looks for the label and decodes it according to the above struct.  So this
  531.  is only for implementations with the above C size definitions.  Our own handler routines
  532.  defined for containers are used to do the positioning and read.
  533.  
  534.  No editoralizing is done on the extracted information.  It is simply passed back to the
  535.  API where the validation is done.  
  536. */
  537.  
  538. static void readLabel_Handler(CMRefCon refCon, CMMagicBytes magicByteSequence,
  539.                                                              CMContainerFlags *flags, CM_USHORT *bufSize,
  540.                                                              CM_USHORT *majorVersion, CM_USHORT *minorVersion,
  541.                                                               CMSize *tocOffset, CMSize *tocSize)
  542. {
  543.     RefConPtr                 p = (RefConPtr)refCon;
  544.     unsigned long            labelSize;
  545.     ContainerLabelFmt theLabel;
  546.  
  547.     /* Seek to the end of the label at the end of the value and read it...                                */
  548.     
  549.     seek_Handler(refCon, -(long)sizeof(ContainerLabelFmt), kCMSeekEnd);
  550.     labelSize = (unsigned long)read_Handler(refCon, (CMPtr)&theLabel,
  551.                                                                                     (CMSize)sizeof(unsigned char),
  552.                                                                                     (CMCount)sizeof(ContainerLabelFmt));
  553.     
  554.     if (labelSize != sizeof(ContainerLabelFmt)) {        /* must have read it all!                            */
  555.         CMError(p->sessionData, "Target container (for pointing value type \"^0\") label could not be read!", p->typeName);
  556.         return;
  557.     }
  558.     
  559.     /* Return all the label info...                                                                                                                */
  560.     
  561.     memcpy(magicByteSequence, theLabel.magicBytes, 8);
  562. #if kCMDefaultEndian
  563.     /* little endian machine */
  564.     if ((theLabel.flags & kCMLittleEndianTwin) == 0) {
  565. #else
  566.     /* big endian machine */
  567.     if (theLabel.flags & kCMLittleEndianTwin) {
  568. #endif
  569.         p->isReverseEndian = 1;
  570.         ODFlipMove(&theLabel.flags, flags, sizeof(CM_USHORT));
  571.         ODFlipMove(&theLabel.bufSize, bufSize, sizeof(CM_USHORT));
  572.         ODFlipMove(&theLabel.majorVersion, majorVersion, sizeof(CM_USHORT));
  573.         ODFlipMove(&theLabel.minorVersion, minorVersion, sizeof(CM_USHORT));
  574.         ODFlipMove(&theLabel.tocOffset, tocOffset, sizeof(CMSize));
  575.         ODFlipMove(&theLabel.tocSize, tocSize, sizeof(CMSize));
  576.     }
  577.     else {
  578.         *flags = (CMContainerFlags)theLabel.flags;
  579.         *bufSize = (CM_USHORT)theLabel.bufSize;
  580.         *majorVersion = (CM_USHORT)theLabel.majorVersion;
  581.         *minorVersion = (CM_USHORT)theLabel.minorVersion;
  582.         *tocOffset = (CMSize)theLabel.tocOffset;
  583.         *tocSize = (CMSize)theLabel.tocSize;
  584.     }
  585. }
  586.  
  587.  
  588. /*---------------------------------------------------------------------------*
  589.  | returnContainerName_Handler - return string that identifies the container |
  590.  *---------------------------------------------------------------------------*
  591.  
  592.  Creates an appropriate string to identify the target container for error inserts.
  593. */
  594.  
  595. static CM_UCHAR *returnContainerName_Handler(CMRefCon refCon)
  596. {
  597.     return ((CM_UCHAR *)((RefConPtr)refCon)->typeName);
  598. }
  599.  
  600.  
  601. /*----------------------------------------------------------------------------*
  602.  | extractData_Handler - extract private Container Manager data from a buffer |
  603.  *----------------------------------------------------------------------------*
  604.  
  605.  This handler is used to extract "internal" Container Manager data previously written to
  606.  the container (e.g., updating data and CMReferences).  1, 2, or 4 bytes (size 8-bit byte)
  607.  "chunks" of data are expected to be copied from a buffer to the data.  Pointers to the
  608.  data and the buffer are passed in.  The buffer can always be assumed large enough to
  609.  supply all the requested data.  The pointer to the data can be assumed to point to a
  610.  CM_UCHAR if size is 1, CM_USHORT if size is 2, and CM_ULONG is size if 4.
  611.  
  612.  The 1, 2, or 4 bytes are, of course, formatted within the CM_UCHAR, CM_USHORT, or CM_ULONG
  613.  as a function of the architecture.  These may be a different size than what is expected
  614.  to be written to the container.  Indeed, it is the potential difference between the
  615.  architecture from the data layout in the  container that this handler must be provided.
  616.  
  617.  The information is stored in the container in a layout private to the Container Manager.
  618.  For example, it is used to extract updating information and the fields of the TOC. It does
  619.  repeated calls to this handler to extract the information it needs from a buffer
  620.  that it loads using the read_Handler.
  621.   
  622.  In this example CM_UCHAR, CM_USHORT and CM_ULONG map directly into the container format
  623.  1, 2, and 4 byte entities.  Hence extracting the data is straight-forward.
  624. */
  625.  
  626. static void extractData_Handler(CMRefCon refCon, CMDataBuffer buffer,
  627.                                                                  CMSize size, CMPrivateData data)
  628. {
  629.     RefConPtr p = (RefConPtr)refCon;
  630.     /* Adkins -- changed from Boolean to CMBoolean */
  631.     CMBoolean    reverseEndian = p->isReverseEndian;
  632.         
  633.     if ((CM_LONG)size < 0) {    /* this means it is endian-ness netural    */
  634.         size = -(CM_LONG)size;
  635.         reverseEndian = 0;
  636.     }
  637.     
  638.     if (reverseEndian)
  639.         ODFlipMove(buffer, data, (size_t)size);                        /* copy the buffer to the data        */
  640.     else
  641.         memcpy(data, buffer, (size_t)size);                                    /* copy the buffer to the data        */
  642. }
  643.  
  644.  
  645. /*--------------------------------------------------------------------------*
  646.  | formatData_Handler - format private Container Manager data into a buffer |
  647.  *--------------------------------------------------------------------------*
  648.  
  649.  This handler is used to format "internal" Container Manager data to be written to
  650.  the container (e.g., updating data and CMReferences).  1, 2, or 4 bytes (size 8-bit byte)
  651.  "chunks" of data are expected to be copied from the data to a buffer.  Pointers to the
  652.  data and the buffer are passed in.  The buffer can always be assumed large enough to hold
  653.  the data.  The pointer to the data can be assumed to point to a CM_UCHAR if size is 1,
  654.  CM_USHORT if size is 2, and CM_ULONG is size if 4.
  655.  
  656.  The 1, 2, or 4 bytes are, of course, stored in the CM_UCHAR, CM_USHORT, or CM_ULONG as a
  657.  function of the architecture.  These may be a different size than what is expected to be
  658.  written to the container.  Indeed, it is the potential difference between the architecture
  659.  from the data layout in the container that this handler must be provided.
  660.  
  661.  The information is stored in the container in a layout PRIVATE to the Container Manager.
  662.  For example, it is used to format the fields of the TOC.  It does repeated calls to this
  663.  handler to format the information it needs into a buffer that is eventually written using
  664.  the write_Handler.
  665.   
  666.  In this example CM_UCHAR, CM_USHORT and CM_ULONG directly map into the container format
  667.  1, 2, and 4 byte entities.  Hence the formatting is straight-forward.
  668. */
  669.  
  670. static void formatData_Handler(CMRefCon refCon, CMDataBuffer buffer,
  671.                                                              CMSize size, CMPrivateData data)
  672. {
  673.     RefConPtr p = (RefConPtr)refCon;
  674.     /* Adkins -- changed from Boolean to CMBoolean */
  675.     CMBoolean    reverseEndian = p->isReverseEndian;
  676.     
  677.     if ((CM_LONG)size < 0) {    /* this means it is endian-ness netural    */
  678.         size = -(CM_LONG)size;
  679.         reverseEndian = 0;
  680.     }
  681.     
  682.     if (reverseEndian)
  683.         ODFlipMove(data, buffer, (size_t)size);                        /* copy the data top the buffer        */
  684.     else
  685.         memcpy(buffer, data, (size_t)size);                                    /* copy the data top the buffer        */
  686. }
  687.